//备注：拷贝代码请加上作者信息
//作者：王海涛
//邮箱：1126471088@qq.com
//版本：V1.0.0
#include "rgb_display_driver.h"
#include "stm32f4xx_ltdc.h"
#include "stm32f4xx_dma2d.h"


/* 屏幕信息 */
static WHT_RGB_Display_Info_t WHT_Display_Info;
/* DMA2D配置结构体加速器 */
static DMA2D_InitTypeDef DMA2D_InitStruct;


/* GPIO初始化 */
static void WHT_GPIO_Init(void)
{
    /* 红色数据线 */
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_R0_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_R1_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_R2_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_R3_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_R4_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_R5_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_R6_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_R7_GPIO_Port, ENABLE);

    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_R0_GPIO_Port, LTDC_R0_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_R1_GPIO_Port, LTDC_R1_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_R2_GPIO_Port, LTDC_R2_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_R3_GPIO_Port, LTDC_R3_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_R4_GPIO_Port, LTDC_R4_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_R5_GPIO_Port, LTDC_R5_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_R6_GPIO_Port, LTDC_R6_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_R7_GPIO_Port, LTDC_R7_GPIO_Pin, Mode_AF_PP);

    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_R0_GPIO_Port, LTDC_R0_GPIO_Pin, LTDC_R0_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_R1_GPIO_Port, LTDC_R1_GPIO_Pin, LTDC_R1_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_R2_GPIO_Port, LTDC_R2_GPIO_Pin, LTDC_R2_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_R3_GPIO_Port, LTDC_R3_GPIO_Pin, LTDC_R3_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_R4_GPIO_Port, LTDC_R4_GPIO_Pin, LTDC_R4_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_R5_GPIO_Port, LTDC_R5_GPIO_Pin, LTDC_R5_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_R6_GPIO_Port, LTDC_R6_GPIO_Pin, LTDC_R6_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_R7_GPIO_Port, LTDC_R7_GPIO_Pin, LTDC_R7_GPIO_AF);

    /* 绿色数据线 */
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_G0_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_G1_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_G2_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_G3_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_G4_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_G5_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_G6_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_G7_GPIO_Port, ENABLE);

    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_G0_GPIO_Port, LTDC_G0_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_G1_GPIO_Port, LTDC_G1_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_G2_GPIO_Port, LTDC_G2_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_G3_GPIO_Port, LTDC_G3_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_G4_GPIO_Port, LTDC_G4_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_G5_GPIO_Port, LTDC_G5_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_G6_GPIO_Port, LTDC_G6_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_G7_GPIO_Port, LTDC_G7_GPIO_Pin, Mode_AF_PP);

    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_G0_GPIO_Port, LTDC_G0_GPIO_Pin, LTDC_G0_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_G1_GPIO_Port, LTDC_G1_GPIO_Pin, LTDC_G1_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_G2_GPIO_Port, LTDC_G2_GPIO_Pin, LTDC_G2_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_G3_GPIO_Port, LTDC_G3_GPIO_Pin, LTDC_G3_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_G4_GPIO_Port, LTDC_G4_GPIO_Pin, LTDC_G4_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_G5_GPIO_Port, LTDC_G5_GPIO_Pin, LTDC_G5_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_G6_GPIO_Port, LTDC_G6_GPIO_Pin, LTDC_G6_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_G7_GPIO_Port, LTDC_G7_GPIO_Pin, LTDC_G7_GPIO_AF);
    
    /* 蓝色数据线 */
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_B0_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_B1_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_B2_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_B3_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_B4_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_B5_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_B6_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_B7_GPIO_Port, ENABLE);

    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_B0_GPIO_Port, LTDC_B0_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_B1_GPIO_Port, LTDC_B1_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_B2_GPIO_Port, LTDC_B2_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_B3_GPIO_Port, LTDC_B3_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_B4_GPIO_Port, LTDC_B4_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_B5_GPIO_Port, LTDC_B5_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_B6_GPIO_Port, LTDC_B6_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_B7_GPIO_Port, LTDC_B7_GPIO_Pin, Mode_AF_PP);

    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_B0_GPIO_Port, LTDC_B0_GPIO_Pin, LTDC_B0_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_B1_GPIO_Port, LTDC_B1_GPIO_Pin, LTDC_B1_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_B2_GPIO_Port, LTDC_B2_GPIO_Pin, LTDC_B2_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_B3_GPIO_Port, LTDC_B3_GPIO_Pin, LTDC_B3_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_B4_GPIO_Port, LTDC_B4_GPIO_Pin, LTDC_B4_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_B5_GPIO_Port, LTDC_B5_GPIO_Pin, LTDC_B5_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_B6_GPIO_Port, LTDC_B6_GPIO_Pin, LTDC_B6_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_B7_GPIO_Port, LTDC_B7_GPIO_Pin, LTDC_B7_GPIO_AF);

    /* 控制信号线 */
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_CLK_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_HSYNC_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_VSYNC_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(LTDC_DE_GPIO_Port, ENABLE);

    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_CLK_GPIO_Port, LTDC_CLK_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_HSYNC_GPIO_Port, LTDC_HSYNC_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_VSYNC_GPIO_Port, LTDC_VSYNC_GPIO_Pin, Mode_AF_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(LTDC_DE_GPIO_Port, LTDC_DE_GPIO_Pin, Mode_AF_PP);

    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_CLK_GPIO_Port, LTDC_CLK_GPIO_Pin, LTDC_CLK_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_HSYNC_GPIO_Port, LTDC_HSYNC_GPIO_Pin, LTDC_HSYNC_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_VSYNC_GPIO_Port, LTDC_VSYNC_GPIO_Pin, LTDC_VSYNC_GPIO_AF);
    WHT_GPIO_BSP.WHT_Set_AFIO(LTDC_DE_GPIO_Port, LTDC_DE_GPIO_Pin, LTDC_DE_GPIO_AF);

    /* 屏控制信号线 */
    WHT_GPIO_BSP.WHT_Set_Clock(RGB_DISPLAY_GPIO_Port, ENABLE);
    WHT_GPIO_BSP.WHT_Set_Clock(RGB_BACK_GPIO_Port, ENABLE);

    WHT_GPIO_BSP.WHT_Set_Pin(RGB_DISPLAY_GPIO_Port, RGB_DISPLAY_GPIO_Pin, Hig); // 开显示
    WHT_GPIO_BSP.WHT_Set_Pin(RGB_BACK_GPIO_Port, RGB_BACK_GPIO_Pin, Hig);       // 开背光

    WHT_GPIO_BSP.WHT_Set_Mode(RGB_DISPLAY_GPIO_Port, RGB_DISPLAY_GPIO_Pin, Mode_OPU_PP);
    WHT_GPIO_BSP.WHT_Set_Mode(RGB_BACK_GPIO_Port, RGB_BACK_GPIO_Pin, Mode_OPU_PP);
}
/* 层初始化 */
static void WHT_Layer_Init(void)
{
    LTDC_Layer_InitTypeDef LTDC_Layer_InitStruct;

    /* 层窗口配置 */
    /* 配置本层的窗口边界，注意这些参数是包含HBP HSW VBP VSW的 */    
	//一行的第一个起始像素，该成员值应用为 (LTDC_InitStruct.LTDC_AccumulatedHBP+1)的值
	LTDC_Layer_InitStruct.LTDC_HorizontalStart = HBP + HSW;
	//一行的最后一个像素，该成员值应用为 (LTDC_InitStruct.LTDC_AccumulatedActiveW)的值
	LTDC_Layer_InitStruct.LTDC_HorizontalStop = HSW+HBP+ WHT_Display_Info.X_Length -1;
	//一列的第一个起始像素，该成员值应用为 (LTDC_InitStruct.LTDC_AccumulatedVBP+1)的值
	LTDC_Layer_InitStruct.LTDC_VerticalStart =  VBP + VSW;
	//一列的最后一个像素，该成员值应用为 (LTDC_InitStruct.LTDC_AccumulatedActiveH)的值
	LTDC_Layer_InitStruct.LTDC_VerticalStop = VSW+VBP+ WHT_Display_Info.Y_Length -1;

    /* Alpha 常数（255完全不透明） */
    LTDC_Layer_InitStruct.LTDC_ConstantAlpha = 0;
    /* 默认颜色配置（配置A、R、G、B）*/
    LTDC_Layer_InitStruct.LTDC_DefaultColorAlpha = 0;
    LTDC_Layer_InitStruct.LTDC_DefaultColorRed = 0;
    LTDC_Layer_InitStruct.LTDC_DefaultColorGreen = 0;
    LTDC_Layer_InitStruct.LTDC_DefaultColorBlue = 0;

    /* 行长度 = 有效高宽度 × 每个像素的字节数 + 3 */
    LTDC_Layer_InitStruct.LTDC_CFBLineLength = ((WHT_Display_Info.X_Length * 2) + 3);
    /* 间距 = 有效宽度 × 每像素的字节数 */
    LTDC_Layer_InitStruct.LTDC_CFBPitch = WHT_Display_Info.X_Length * 2;
    /* 行数 */
    LTDC_Layer_InitStruct.LTDC_CFBLineNumber = WHT_Display_Info.Y_Length;
    
    /* 配置第一层 */
    /* 像素格式配置*/
    LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB565;
    /* 帧缓冲区地址 */
    LTDC_Layer_InitStruct.LTDC_CFBStartAdress = RGB_Frame_Buffer1;
    /* 配置混合因子 */
    LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_CA;
    LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_CA;
    /* 初始化LTDC第1层 */
    LTDC_LayerInit(LTDC_Layer1, &LTDC_Layer_InitStruct);


    /* 配置第二层 */
    /* 像素格式配置 */
    LTDC_Layer_InitStruct.LTDC_PixelFormat = LTDC_Pixelformat_RGB565;
    /* 帧缓冲区地址 */
    LTDC_Layer_InitStruct.LTDC_CFBStartAdress = RGB_Frame_Buffer2;
    /* 配置混合因子 */
    LTDC_Layer_InitStruct.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA;
    LTDC_Layer_InitStruct.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA;
    /* 初始化LTDC第2层 */
    LTDC_LayerInit(LTDC_Layer2, &LTDC_Layer_InitStruct);

    /* LTDC配置重新加载 */
    LTDC_ReloadConfig(LTDC_IMReload);

    /* 启用前景层和背景层 */
    LTDC_LayerCmd(LTDC_Layer1, ENABLE);
    LTDC_LayerCmd(LTDC_Layer2, ENABLE);

    /* LTDC配置重新加载 */
    LTDC_ReloadConfig(LTDC_IMReload);

    /* 抖动激活 */
    LTDC_DitherCmd(ENABLE);
}
/* 屏初始化 */
static void WHT_RGB_Display_Init(void)
{
    LTDC_InitTypeDef LTDC_InitStruct;
    WHT_RGB_Display_Info_t WHT_RGB_Display_Info = WHT_RGB_DEFAULT_INFO;

    /* 初始化屏幕参数 */
    WHT_Display_Info = WHT_RGB_Display_Info;

    /* 开启LTDC时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_LTDC, ENABLE);

    /* 开启DMA2D时钟 */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2D, ENABLE);

    /* 配置RGB控制引脚 */
    WHT_GPIO_Init();

    /* 配置SDRAM提供缓存给RGB */
    WHT_SDRAM_BSP.WHT_Register();

    /* 配置 PLLSAI 分频器，它的输出作为像素同步时钟CLK*/
    /* PLLSAI_VCO 输入时钟 = HSE_VALUE/PLL_M = 1 Mhz */
    /* PLLSAI_VCO 输出时钟 = PLLSAI_VCO in * PLLSAI_N = 384 Mhz */
    /* PLLLCDCLK = PLLSAI_VCO out / PLLSAI_R = 384 / 6 Mhz */
    /* LTDC 时钟频率 = PLLLCDCLK / DIV = 384 / 4 / 4 = 24 Mhz */
    /* 以下函数三个参数分别为：PLLSAIN,PLLSAIQ,PLLSAIR，其中PLLSAIQ与LTDC无关*/
    RCC_PLLSAIConfig(384, 7, 4);
    //RCC_PLLSAIConfig(320, 4, 2);
    /*以下函数的参数为DIV值*/
    RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div4);

    /* 启用PLLSAI时钟 */
    RCC_PLLSAICmd(ENABLE);
    /* 等待PLLSAI激活 */
    while (RCC_GetFlagStatus(RCC_FLAG_PLLSAIRDY) == RESET);

    /* 极性配置 */
    /* 将水平同步极性初始化为低电平有效 */
    LTDC_InitStruct.LTDC_HSPolarity = LTDC_HSPolarity_AL;
    /* 将垂直同步极性初始化为低电平有效 */
    LTDC_InitStruct.LTDC_VSPolarity = LTDC_VSPolarity_AL;
    /* 初始化数据使极性为低电平有效 */
    LTDC_InitStruct.LTDC_DEPolarity = LTDC_DEPolarity_AL;
    /* 将像素时钟极性初始化为输入像素时钟 */
    LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IPC;

    /* 配置RGB背景颜色R、G、B */
    LTDC_InitStruct.LTDC_BackgroundRedValue = 0;
    LTDC_InitStruct.LTDC_BackgroundGreenValue = 0;
    LTDC_InitStruct.LTDC_BackgroundBlueValue = 0;

    /* 时间参数配置 */
    /* 配置行同步信号宽度 */
    LTDC_InitStruct.LTDC_HorizontalSync = HSW - 1;
    LTDC_InitStruct.LTDC_AccumulatedHBP = HSW + HBP - 1;
    /* 配置有效像素宽度 */
    LTDC_InitStruct.LTDC_AccumulatedActiveW = HSW + HBP + WHT_Display_Info.X_Length - 1;
    /* 配置总像素宽度 */
    LTDC_InitStruct.LTDC_TotalWidth = HSW + HBP + WHT_Display_Info.X_Length + HFP - 1; 

    /* 配置垂直同步信号宽度 */
    LTDC_InitStruct.LTDC_VerticalSync = VSW - 1;
    LTDC_InitStruct.LTDC_AccumulatedVBP = VSW + VBP - 1;
    /* 配置有效像素高度 */
    LTDC_InitStruct.LTDC_AccumulatedActiveH = VSW + VBP + WHT_Display_Info.Y_Length -1;
    /* 配置总像素高度 */
    LTDC_InitStruct.LTDC_TotalHeigh = VSW + VBP + WHT_Display_Info.Y_Length + VFP - 1;
    LTDC_Init(&LTDC_InitStruct);

    WHT_Layer_Init();

    DMA2D_DeInit();
    
    LTDC_Cmd(ENABLE);
}


/* 区域内填充颜色 */
static void WHT_RGB_Fill_Color(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t rgb565)
{
    /* 防越界 */
    if (sx > ex || sy > ey) return;
    if (ex >= WHT_Display_Info.X_Length) ex = WHT_Display_Info.X_Length - 1;
    if (ey >= WHT_Display_Info.Y_Length) ey = WHT_Display_Info.Y_Length - 1;

    DMA2D_InitStruct.DMA2D_Mode = DMA2D_R2M;
    DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565;
    DMA2D_InitStruct.DMA2D_OutputRed = (rgb565 & 0xf800) >> 11;
    DMA2D_InitStruct.DMA2D_OutputGreen = (rgb565 & 0x07e0) >> 5;
    DMA2D_InitStruct.DMA2D_OutputBlue = rgb565 & 0x001f;
    /* 透明度 */
    DMA2D_InitStruct.DMA2D_OutputAlpha = 0x0F;
    /* 目标地址 - 要填充的显存 */
    DMA2D_InitStruct.DMA2D_OutputMemoryAdd = WHT_Display_Info.New_Frame_Buffer_Addr + (WHT_Display_Info.X_Length * sy + sx) * 2;
    /* 目标行偏移 */
    DMA2D_InitStruct.DMA2D_OutputOffset = WHT_Display_Info.X_Length - (ex - sx + 1);
    /* 行数 */
    DMA2D_InitStruct.DMA2D_NumberOfLine = ey - sy + 1;
    /* 每行像素数 */
    DMA2D_InitStruct.DMA2D_PixelPerLine = ex - sx + 1;
    
    /* 重新初始化参数 */
    DMA2D_Init(&DMA2D_InitStruct);
    /* 开始传输 */
    DMA2D_StartTransfer();
    /* 等待传输完成 */
    while(DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET);
    /* 清除传输完成标志 */
    DMA2D_ClearFlag(DMA2D_FLAG_TC);
}
/* 区域内填充颜色缓存 */
static void WHT_RGB_Fill_Color_Buffer_DMA2D(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, const uint16_t* input_buf)
{
    static DMA2D_FG_InitTypeDef FG_InitStruct;

    /* 参数校验和防越界 */
    if (sx > ex || sy > ey) return;
    if (ex >= WHT_Display_Info.X_Length) ex = WHT_Display_Info.X_Length - 1;
    if (ey >= WHT_Display_Info.Y_Length) ey = WHT_Display_Info.Y_Length - 1;
    
    /* 配置DMA2D主结构体 */
    DMA2D_InitStruct.DMA2D_Mode = DMA2D_M2M;             // 内存到内存模式
    DMA2D_InitStruct.DMA2D_CMode = DMA2D_RGB565;         // 输出颜色格式
    DMA2D_InitStruct.DMA2D_OutputMemoryAdd = WHT_Display_Info.New_Frame_Buffer_Addr + (WHT_Display_Info.X_Length * sy + sx) * 2;//目标地址
    DMA2D_InitStruct.DMA2D_OutputOffset = WHT_Display_Info.X_Length - (ex - sx + 1);  // 目标行偏移
    DMA2D_InitStruct.DMA2D_NumberOfLine = ey - sy + 1;   // 传输行数
    DMA2D_InitStruct.DMA2D_PixelPerLine = ex - sx + 1;   // 每行像素数
    // M2M模式下这些值不影响但必须设置
    DMA2D_InitStruct.DMA2D_OutputRed = 0;
    DMA2D_InitStruct.DMA2D_OutputGreen = 0;
    DMA2D_InitStruct.DMA2D_OutputBlue = 0;
    DMA2D_InitStruct.DMA2D_OutputAlpha = 0x0F;
    DMA2D_Init(&DMA2D_InitStruct);

    /* 配置前景层（源数据） */
    FG_InitStruct.DMA2D_FGCM = CM_RGB565;                     // 源颜色格式，必须与输入缓冲区匹配
    FG_InitStruct.DMA2D_FGMA = (uint32_t)input_buf;           // 源数据地址
    FG_InitStruct.DMA2D_FGO = 0;                              // 源行偏移：如果输入缓冲区是连续的完整图像，这里填0                      
    FG_InitStruct.DMA2D_FGPFC_ALPHA_MODE = NO_MODIF_ALPHA_VALUE; // 不修改Alpha值
    FG_InitStruct.DMA2D_FGPFC_ALPHA_VALUE = 0xFF;             // 完全不透明
    DMA2D_FGConfig(&FG_InitStruct);
    
    /* 关闭CLUT自动加载,开启就会卡死 */
    DMA2D_FGStart(DISABLE);
    /* 启动传输 */
    DMA2D_StartTransfer();
    /* 等待传输完成 */
    while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET);
    /* 清除标志位 */
    DMA2D_ClearFlag(DMA2D_FLAG_TC);
}
/* 区域内填充颜色缓存 */
static void WHT_RGB_Fill_Color_Buffer(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, const uint16_t* intput_buf)
{
    /* 参数校验和防越界 */
    if (sx > ex || sy > ey) return;
    if (ex >= WHT_Display_Info.X_Length) ex = WHT_Display_Info.X_Length - 1;
    if (ey >= WHT_Display_Info.Y_Length) ey = WHT_Display_Info.Y_Length - 1;
    
    volatile uint16_t (*Xaddr)[RGB_PIXEL_WIDTH] = (volatile uint16_t(*)[RGB_PIXEL_WIDTH])WHT_Display_Info.New_Frame_Buffer_Addr;

    for (size_t y = sy; y <= ey; y++)
    {
        for (size_t x = sx; x <= ex; x++)
        {
            Xaddr[y][x] = *intput_buf++;
        }
    }
}



/* 设置图层 */
static void WHT_RGB_Set_Layer(WHT_RGB_Layer_enum layer)
{
    if (layer == RGB_Backround_Layer)
    {
        WHT_Display_Info.New_Frame_Buffer_Addr = RGB_Frame_Buffer1;
        WHT_Display_Info.New_Layer = RGB_Backround_Layer;
    }
    else
    {
        WHT_Display_Info.New_Frame_Buffer_Addr = RGB_Frame_Buffer2;
        WHT_Display_Info.New_Layer = RGB_Foreground_Layer;
    }
}
/* 设置透明度 */
static void WHT_RGB_Set_Transparency(uint8_t transparency)
{
    LTDC_LayerAlpha(WHT_Display_Info.New_Layer == RGB_Backround_Layer ? LTDC_Layer1 : LTDC_Layer2, transparency);
    LTDC_ReloadConfig(LTDC_IMReload);
}
/* 设置屏幕显示窗口 */ 
static void WHT_RGB_Set_Display_Window(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey)
{
    /* 重新配置第x层的位置 */
    LTDC_LayerPosition(WHT_Display_Info.New_Layer == RGB_Backround_Layer ? LTDC_Layer1 : LTDC_Layer2, sx, sy);
    LTDC_ReloadConfig(LTDC_IMReload);

    /* 重新配置第x层的大小 */
    LTDC_LayerSize(WHT_Display_Info.New_Layer == RGB_Backround_Layer ? LTDC_Layer1 : LTDC_Layer2, ex - sx + 1, ey - sy + 1);
    LTDC_ReloadConfig(LTDC_IMReload);
}
/* 开启颜色键控 */
static void WHT_RGB_Start_Color_Keying(uint32_t rgb)
{
    LTDC_ColorKeying_InitTypeDef   LTDC_colorkeying_InitStruct;

    /* 配置颜色键控 */
    LTDC_colorkeying_InitStruct.LTDC_ColorKeyRed = (0xFF0000 & rgb) >> 16;
    LTDC_colorkeying_InitStruct.LTDC_ColorKeyGreen = (0x00FF00 & rgb) >> 8;
    LTDC_colorkeying_InitStruct.LTDC_ColorKeyBlue = 0x0000FF & rgb;

    /* 启用图层x的颜色键控 */
    LTDC_ColorKeyingConfig(WHT_Display_Info.New_Layer == RGB_Backround_Layer ? LTDC_Layer1 : LTDC_Layer2, &LTDC_colorkeying_InitStruct, ENABLE);
    LTDC_ReloadConfig(LTDC_IMReload);
}
/* 关闭颜色键控 */
static void WHT_RGB_Stop_Color_Keying(void)
{
    LTDC_ColorKeying_InitTypeDef   LTDC_colorkeying_InitStruct;

    /* 禁用图层x的颜色键控 */
    LTDC_ColorKeyingConfig(WHT_Display_Info.New_Layer == RGB_Backround_Layer ? LTDC_Layer1 : LTDC_Layer2, &LTDC_colorkeying_InitStruct, DISABLE);
    LTDC_ReloadConfig(LTDC_IMReload);
}
/* 设置屏幕状态 */
static void WHT_RGB_Set_Display_State(uint8_t back_state, uint8_t display_state)
{
    WHT_GPIO_BSP.WHT_Set_Pin(RGB_BACK_GPIO_Port, RGB_BACK_GPIO_Pin, back_state ? Hig : Low);
    WHT_GPIO_BSP.WHT_Set_Pin(RGB_DISPLAY_GPIO_Port, RGB_DISPLAY_GPIO_Pin, display_state ? Hig : Low);
}


/*设置行和行位置*/
static uint8_t WHT_RGB_Set_Font_Row(uint16_t row, uint16_t pos_x)
{
    uint8_t Max_Display_Font_Row_Number;//屏幕最大显示字体行数
	uint8_t Idle_Row_Number;            //屏幕空闲行数
	
	Max_Display_Font_Row_Number = WHT_Display_Info.Y_Length / WHT_Display_Info.Font->Height;
	Idle_Row_Number = WHT_Display_Info.Y_Length % WHT_Display_Info.Font->Height;
	WHT_Display_Info.New_Pos_X = pos_x >= WHT_Display_Info.X_Length ? 0 : pos_x;
	WHT_Display_Info.New_Pos_Y = row % Max_Display_Font_Row_Number * WHT_Display_Info.Font->Height + Idle_Row_Number;//屏幕上方字体空闲区域
	return Idle_Row_Number;
}
/* 检测回车换行 */
static void WHT_RGB_Check_Line_Feed(char intput_char)
{
	if ((intput_char == '\n') || (intput_char == '\r'))
	{
		if (WHT_Display_Info.New_Pos_Y + WHT_Display_Info.Font->Height >= WHT_Display_Info.Y_Length)
			WHT_RGB_Set_Font_Row(0, 0);//归0
		else
		{
			WHT_Display_Info.New_Pos_X = 0;
			WHT_Display_Info.New_Pos_Y += WHT_Display_Info.Font->Height;
		}
	}
}
/* 显示字符串 最大支持250点字体 */
static void WHT_RGB_Puts_ASCII(const char* intput_char)
{
    static uint16_t Tx_Buffer[50 * 50];
	static uint32_t datax;
	static uint8_t* start_pos;
	uint32_t row,col;
	
	do
	{
		if( (*intput_char < WHT_Display_Info.Font->Start_Value) || (*intput_char > WHT_Display_Info.Font->End_Value) )
		{
			WHT_RGB_Check_Line_Feed(*intput_char);
			return;
		}
		start_pos = &WHT_Display_Info.Font->Buffer[(*intput_char - WHT_Display_Info.Font->Start_Value) * WHT_Display_Info.Font->Width_Byte * WHT_Display_Info.Font->Height];//字符位置
		for (row = 0; row < WHT_Display_Info.Font->Height; row++)
		{
			switch(WHT_Display_Info.Font->Pixel_Size)
			{
			case  5 *  8:datax = start_pos[row] >> (8 - 5);break;
			case  6 * 12:datax = start_pos[row] >> (8 - 6);break;
			case  8 * 16:datax = start_pos[row] >> (8 - 8);break;
			case 12 * 24:datax = (((uint16_t*)start_pos)[row] >> 8 | ((uint16_t*)start_pos)[row] << 8) >> (16 - 12);break;
			}
			for (col = 0; col < WHT_Display_Info.Font->Width; col++)
			{
				if( datax & (1 << (WHT_Display_Info.Font->Width - 1 - col)))
					Tx_Buffer[row * WHT_Display_Info.Font->Width + col] = WHT_Display_Info.Font_Color;//填充前景色颜色
				else
					Tx_Buffer[row * WHT_Display_Info.Font->Width + col] = WHT_Display_Info.BK_Color;  //填充背景色
			}
		}
		if(WHT_Display_Info.New_Pos_X + WHT_Display_Info.Font->Width >= WHT_Display_Info.X_Length)
			WHT_RGB_Check_Line_Feed('\n');
		WHT_RGB_Fill_Color_Buffer(WHT_Display_Info.New_Pos_X, WHT_Display_Info.New_Pos_Y, WHT_Display_Info.New_Pos_X + WHT_Display_Info.Font->Width - 1, WHT_Display_Info.New_Pos_Y + WHT_Display_Info.Font->Height - 1, Tx_Buffer);
		WHT_Display_Info.New_Pos_X += WHT_Display_Info.Font->Width;//指针右移1个字符
		intput_char++;
	}while(*intput_char);
}
/* 清除行 */
static void WHT_RGB_Clear_Row(uint8_t row)
{
	uint32_t Max_Font_Row_Count;
	
	WHT_RGB_Set_Font_Row(row, 0);                    //设置指针位置
	Max_Font_Row_Count = WHT_Display_Info.X_Length / WHT_Display_Info.Font->Width;//获取每行最大显示字体个数
	while (Max_Font_Row_Count--)
	{
		WHT_RGB_Puts_ASCII(" ");
	}
	WHT_RGB_Set_Font_Row(WHT_Display_Info.New_Pos_Y, 0); //重置指针位置
}


const WHT_RGB_Display_Driver_t WHT_RGB_Display_Driver =
{
    .WHT_Info               = &WHT_Display_Info,
    .WHT_Init               = WHT_RGB_Display_Init,
    .WHT_Set_Layer          = WHT_RGB_Set_Layer,
    .WHT_Set_Transparency   = WHT_RGB_Set_Transparency,
    .WHT_Set_Display_Window = WHT_RGB_Set_Display_Window,
    .WHT_Set_Display_State  = WHT_RGB_Set_Display_State,
    .WHT_Start_Color_Keying = WHT_RGB_Start_Color_Keying,
    .WHT_Stop_Color_Keying  = WHT_RGB_Stop_Color_Keying,
    .WHT_Set_Font_Row       = WHT_RGB_Set_Font_Row,
    .WHT_Clear_Row          = WHT_RGB_Clear_Row,
    .WHT_Puts               = WHT_RGB_Puts_ASCII,
    .WHT_Fill_Color         = WHT_RGB_Fill_Color,
    //.WHT_Fill_Color_Buffer  = WHT_RGB_Fill_Color_Buffer,
    .WHT_Fill_Color_Buffer  = WHT_RGB_Fill_Color_Buffer_DMA2D,
};
